const chalk = require("chalk");

const Koa = require("koa");
const Router = require("koa-router");
const koaStatic = require("koa-static");
const compose = require("koa-compose");
const { koaBody } = require("koa-body");
const path = require("path");

const cors = require("koa2-cors");
const { getIpAddress } = require("./tools");
const routes = require("./routes");
const sequelize = require("./database");
const { randomInt, wait } = require("./tools");

const app = new Koa();
const router = new Router();

app.use(async (ctx, next) => {
  ctx.sequelize = sequelize;
  await next();
});

const mockTrueServer = async (ctx, next) => {
  // await wait(randomInt(500, 2500));
  await next();
};

const errorHandler = async (ctx, next) => {
  try {
    await next();
  } catch (err) {
    console.log("error info ======>", err.statusCode, err.name);
    if (err.name === "SequelizeUniqueConstraintError" && err.errors.length) {
      ctx.response.status = 400;
      ctx.response.body = {
        error: err.errors[0],
      };
    } else {
      ctx.response.status = err.status || err.statusCode || 500;
      ctx.response.body = {
        error: err.name,
      };
    }

    ctx.app.emit("error", err, ctx);
  }
};

const responseHandler = async (ctx, next) => {
  ctx.success = function (data) {
    ctx.body = {
      status: 1,
      msg: "success",
      ...data,
    };
  };
  ctx.fail = function (msg, code) {
    ctx.response.statusCode = code;
    ctx.body = {
      status: 0,
      msg,
      code,
    };
  };

  await next();
};

const middlewares = compose([
  cors({
    origin: "*", // 允许所有域名访问
    credentials: true, // 允许携带cookie
  }),
  koaStatic(path.join(__dirname, "static")),
  koaBody(),
  responseHandler,
  errorHandler,
  mockTrueServer,
]);

app.use(middlewares);

/**
 * desc: register all routes here:
 * methods: get | put | post | patch | delete | del
 * */
Object.entries(routes).forEach(([name, route]) => {
  if (Array.isArray(route)) {
    // curd 模式
    for (let [type, handler] of route) {
      router["post"](`/${route.model}/${type}`, handler);
    }
  } else {
    router[route.method ?? "get"](
      `/${route.path ?? name}`,
      typeof route === "function" ? route : route.handler
    );
  }
});

router.get("/", async (ctx) => {
  ctx.response.type = "html";
  const routesArray = Array.from(
    new Set(router.stack.map((layer) => layer))
  ).map((it) => {
    const methods = JSON.stringify(it.methods);
    let { params, query, body, mark } = it.stack[0] || {};
    params = params ? JSON.stringify(params) : "";
    query = query ? JSON.stringify(query) : "";
    mark = mark ? JSON.stringify(mark) : "";
    body = body
      ? Object.entries(body).map(
          ([k, v]) => `<div style="text-indent: 50px;">${k}: ${v}</div>`
        )
      : "";

    if (it.path === "/" || it.path.startsWith("/service-")) return "";
    return `<li> 
      <a href='${it.path}'>${it.path}</a>
      <span>${methods}</span>
      <span>${params}</span>
      <span>${query}</span>
      <span>${mark}</span>
      
      ${
        body
          ? `<div style="margin-left: 50px;">body:{ ${body.join("")} }</div>`
          : ""
      }
    </li>`;
  });
  routesArray.unshift(`<div><a href='/'>API Book:</a></div>`);
  ctx.response.body = routesArray.join("");
});

app.use(router.routes()).use(router.allowedMethods());

const port = 9000;
app.listen(port, () => {
  console.log(
    chalk.green(`http://127.0.0.1:${port}/`),
    "or",
    chalk.green(`http://${getIpAddress()}:${port}/`)
  );
});

app.on("error", (err, ctx) => console.error("server error ====> \r", err));
